#include "stdTcpService.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cstring>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>


/* 设置监听 */
void StdTcpServer::setListen(int port)
{
    // 创建通信文件描述符
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1)
    {
        perror("socket create error:");
        _exit(-1);
    }
    /* 设置地址 */
    int optVal = 1;
    /* 端口复用 多个进程监听同一个端口*/
    int ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optVal, sizeof(optVal));
    if (ret == -1)
    {
        perror("setsockopt error");
        exit(1);
    }
    // 绑定服务器的IP和端口
    sockaddr_in localAddress;
    // 清除脏数据
    memset(&localAddress, 0, sizeof(localAddress));
    // 地址族
    localAddress.sin_family = AF_INET;         // ipv4
    localAddress.sin_port = htons(port);       // 端口
    localAddress.sin_addr.s_addr = INADDR_ANY; // 设置IP地址 INADDR_ANY:通常为0.0.0.0，代表绑定到所有可用的网络接口 addr(地址)

    // 绑定
    ret = bind(sockfd, (const sockaddr *)&localAddress, sizeof(localAddress));
    if (ret == -1)
    {
        perror("bind error:");
        _exit(-1);
    }

    // 监听
    ret = listen(sockfd, 10);
    if (ret == -1)
    {
        perror("listen error:");
        _exit(-1);
    }
    /* 维护socket句柄 */
    m_sockfd = sockfd;
    /* 维护端口属性 */
    m_port = port;
}

/* 得到与客户端的句柄 */
std::shared_ptr<StdTcpSocket> StdTcpServer::getClientSock()
{
    // 阻塞接受客户端的连接
    int connfd = accept(m_sockfd, NULL, NULL);
    if (connfd == -1)
    {
        perror("accept error");
        _exit(-1);
    }
    std::cout << "有新客户端连接,connfd: " << connfd << std::endl;

    std::shared_ptr<StdTcpSocket> clientSock = std::make_shared<StdTcpSocket>();
    clientSock->setAttrConnfd(connfd);
    return clientSock;
}

/* 连接服务器 */
bool StdTcpSocket::connectToServer(const char * ip,int port)
{
    //创建通信文件描述符
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd == -1)
    {
        perror("socket create error:");
        return false;
    }

    //连接服务器
    sockaddr_in serverAddress;
    memset(&serverAddress,0,sizeof(serverAddress));
    //地址族
    serverAddress.sin_family = AF_INET; //ipv4
    //端口
    serverAddress.sin_port = htons(port);
    //服务器IP
    const char * serverIp = ip;
    inet_pton(AF_INET,serverIp,&(serverAddress.sin_addr.s_addr));
    //连接服务器
    int ret = connect(sockfd,(const sockaddr *)&serverAddress,sizeof(serverAddress));
    if(ret == -1)
    {
        perror("connet error");
        return false;
    }
    /* 维护sockfd句柄 */
    m_connfd = sockfd;
    return true;
}

/* 设置通信句柄 */
void StdTcpSocket::setAttrConnfd(int connfd)
{
    this->m_connfd = connfd;
}

/* 发送数据 */
int StdTcpSocket::sendMessage(const void * sendMsg,size_t size)
{
    /* 发送数据大小 */
    int writeBytes = write(m_connfd,&size,sizeof(size_t));
    /* 发送数据本身 */
    writeBytes = write(m_connfd,sendMsg,size);
    return writeBytes;
}

/* 发送数据 */
int StdTcpSocket::sendMessage(const std::string & sendMsg)
{
    return sendMessage(sendMsg.c_str(),sendMsg.size());
}

/* 接收数据 */
int StdTcpSocket::recvMessage(void * buf,size_t size)
{
    return 0;
}

/* 接收数据 */
int StdTcpSocket::recvMessage(std::string &recvMsg)
{
    /* 1、先得到数据包的大小 */
    size_t dataSize = 0;
    int readBytes = read(m_connfd,&dataSize,sizeof(size_t));
    if(readBytes < 0)
    {
        perror("read error");
        return -1;
    }

    /* 2、再获得数据本身 */
    char * msg = new char[dataSize + 1];
    if(nullptr == msg)
    {
        perror("msg new error");
        exit(1);
    }
    memset(msg,0,sizeof(char)*(dataSize + 1));

    size_t totalReceived = 0;
    readBytes = 0;
    while(totalReceived < dataSize)
    {
        readBytes = read(m_connfd,msg + totalReceived,dataSize - totalReceived);
        if(readBytes < 0)
        {
            perror("read connfd error");
            delete []msg;
            return readBytes;
        }
        else if(readBytes == 0)
        {
            delete []msg;
            return readBytes;
        }
        totalReceived += readBytes;
    }
    /* 赋值 */
    recvMsg = msg;

    /* 释放堆空间 */
    delete []msg;

    return dataSize;
}

